﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Seagull.Configuration;
using Seagull.Events;
using Seagull.Events.Sms;
using Seagull.Generic;
using Seagull.Model.Alerts;
using Seagull.Model.Sms;

namespace Seagull.ZHD1X
{
    public class SmsSendManager : IModule
    {
        #region Singleton

        private static readonly Lazy<SmsSendManager> _instance =
            new Lazy<SmsSendManager>(() => new SmsSendManager());

        public static SmsSendManager Instance
        {
            get { return _instance.Value; }
        }

        #endregion

        private const int ThreadSleepIntervalOnError = 4000;
        private Thread _messageSendThread;
        private readonly Timer _resendMessageTimer;
        private readonly ConcurrentQueue<SmsMessage> _smsMessageQueue = new ConcurrentQueue<SmsMessage>();
        private readonly SmsSender _smsSender = new SmsSender();

        private readonly object _sendSmsSyncRoot = new object();
        private readonly object _syncRoot = new object();
        private volatile bool _hasStopped;
        
        private volatile bool _keepRunning;
        private RegistrationToken _eventRegistrationToken;

        private SmsSendManager()
        {
            _resendMessageTimer = new Timer(ResendFailedMessages);
        }

        public void Start()
        {
            if(_messageSendThread != null)
            {
                throw new InvalidOperationException("The Message Send Thread is already running!");
            }
            _keepRunning = true;
            _hasStopped = false;
            _messageSendThread = new Thread(MessageSendThreadProc) { Name = "SmsMessageSendThread" };
            _messageSendThread.Start();
            _resendMessageTimer.Change(0, Config.Instance.SmsAlertConfig.AlarmSendCheckIntervalMinutes * 60 * 1000);
            _eventRegistrationToken = SendSmsMessageEvent.Instance.Register(OnSendSmsMessage);
        }

        public void Stop()
        {
            lock(_syncRoot)
            {
                if (!_hasStopped)
                {
                    SendSmsMessageEvent.Instance.UnRegister(_eventRegistrationToken);
                    _resendMessageTimer.Change(-1, -1);
                    _keepRunning = false;
                    if (_messageSendThread != null && _messageSendThread.IsAlive && !_messageSendThread.Join(ThreadSleepIntervalOnError))
                    {
                        _messageSendThread.Abort();
                    }
                    _messageSendThread = null;

                    // 将队列中尚未发送的短信记录到发送失败的短信列表中
                    if (!_smsMessageQueue.IsEmpty)
                    {
                        RecordFailedMessages(_smsMessageQueue);
                    }
                    _hasStopped = true;
                }
            }
        }

        private void OnSendSmsMessage(SmsMessage message)
        {
            _smsMessageQueue.Enqueue(message);
        }

        private void MessageSendThreadProc()
        {
            const int sleepInterval = 2000;
            while(_keepRunning)
            {
                try
                {
                    SmsMessage message;
                    if(_smsMessageQueue.TryDequeue(out message))
                    {
                        SendSmsMessage(message);
                    }
                    else
                    {
                        Thread.Sleep(sleepInterval);
                    }
                }
                catch(ThreadAbortException)
                {
                    // Actually this is unnecessary because the ThreadAbortException will be rethrown at the end of catch block.
                    break;
                }
                catch(Exception exception)
                {
                    LogOnlyAlertEvent.Instance.Publish(new GenericAlert
                    {
                        AlertContent = "短信发送模块运行发生错误！",
                        AlertDetail = exception.ToString(),
                    });
                    Thread.Sleep(ThreadSleepIntervalOnError);
                }
            }
        }

        private void SendSmsMessage(SmsMessage message)
        {
            try
            {
                var failedRecipients = SendMessage(message);
                // 记录发送失败的收信人和内容
                if(failedRecipients.Any())
                {
                    RecordFailedMessage(new SmsMessage(message.Content, failedRecipients));
                    LogOnlyAlertEvent.Instance.Publish(new GenericAlert
                    {
                        AlertContent = string.Format("向{0}发送短信失败", string.Join(",", failedRecipients)),
                        AlertDetail = string.Format("短信内容：{0}", message.Content),
                    });
                }
            }
            catch(Exception exception)
            {
                // 记录所有的收信人
                RecordFailedMessage(message);
                LogOnlyAlertEvent.Instance.Publish(new GenericAlert
                {
                    AlertContent = string.Format("发送短信时发生错误：{0}", exception.GetType().Name),
                    AlertDetail = string.Format("短信内容: {0}; 错误详情： {1}", message.Content, exception.ToString())
                });
            }
        }

        /// <summary>
        ///     返回发送失败的号码
        /// </summary>
        /// <param name="message"> </param>
        /// <returns> </returns>
        public string[] SendMessage(SmsMessage message)
        {
            var failedRecipients = new List<string>();
            foreach(var recipient in message.Recipients)
            {
                foreach(var contentPart in message.ContentParts)
                {
                    var success = SendSingleSms(contentPart, recipient);
                    if(!success)
                    {
                        failedRecipients.Add(recipient);
                    }
                }
            }
            return failedRecipients.ToArray();
        }

        private bool SendSingleSms(string content, string recipient)
        {
            lock(_sendSmsSyncRoot)
            {
                try
                {
                    _smsSender.Open(Config.Instance.SmsAlertConfig.Port, Config.Instance.SmsAlertConfig.Rate);
                    return _smsSender.Send(recipient, content);
                }
                finally
                {
                    _smsSender.Close();
                }
            }
        }

        /// <summary>
        ///     记录发送失败的信息，将在下次重试。
        /// </summary>
        /// <param name="smsMessage"> </param>
        private void RecordFailedMessage(SmsMessage smsMessage)
        {
            FailedSmsMessages.Change(alerts => alerts.Messages.Add(smsMessage));
        }

        /// <summary>
        ///     记录发送失败的信息，将在下次重试。
        /// </summary>
        /// <param name="smsMessages"> </param>
        private void RecordFailedMessages(IEnumerable<SmsMessage> smsMessages)
        {
            FailedSmsMessages.Change(alerts => alerts.Messages.AddRange(smsMessages));
        }

        private void ResendFailedMessages(object state)
        {
            try
            {
                IEnumerable<SmsMessage> smsMessages = FailedSmsMessages.Change(failed =>
                {
                    // 取出所有未成功的信息并从文件中删除
                    var messages = failed.Messages.ToArray();
                    failed.Messages.Clear();
                    return messages;
                });
                foreach(var smsMessage in smsMessages)
                {
                    SendSmsMessage(smsMessage);
                }
            }
            catch(Exception exception)
            {
                // 防止程序崩溃
                LogOnlyAlertEvent.Instance.Publish(new ApplicationAlert(exception, "重试发送失败的报警短信"));
            }
        }
    }
}